<?php
require_once 'models/publisher.inc';
require_once 'models/direct.inc';
require_once 'core/utils/repository.inc';

class TYandexDirectException extends TException {
	const ERR_NO_SITE_PAGE = 80001;
	const ERR_NO_PAGE_URL = 80002;
	const ERR_NO_BANNER = 80003;
	
	protected function getMessageText($msgcode){
		switch ($msgcode){
			case self::ERR_NO_SITE_PAGE:return 'Item was not published on shop frontend!';break;
			case self::ERR_NO_PAGE_URL:return 'Product page link was not obtained from showcase!';break;
			case self::ERR_NO_BANNER:return 'Banner not obtained from Direct after creation!';break;
		}
		return '';
	}
}

class TYandexDirectBannerItem extends TCandyItem {
	private $_phrase_list_;
	
	private $_remote_data_;
	
	private $_campaign_data_;
	
	
	private function _load_remote(){
		if (!isset($this->_remote_data_))
			$this->_remote_data_ = $this->Model->GetBannerInfo($this->Base);
	}
	
	private function _load_campaign(){
		if (!isset($this->_campaign_data_) && $this->Campaign)
			$this->_campaign_data_ = $this->Model->GetCampaignInfo($this->Campaign);
	}
	
	public function __get($nm){
		switch ($nm){
			case 'PhraseList':{
				if (!isset($this->_phrase_list_)){
					if ($this->Phrases)
						$this->_phrase_list_ = json_decode($this->Phrases,true);
					else
						$this->_phrase_list_ = array();
				}
				return $this->_phrase_list_;
			}break;
			case 'NextNewPhraseId':{
				$max = 0;
				foreach ($this->PhraseList as $key=>$val)
					if (preg_match('/^'.$this->ItemId().'_(\d+)/',$key,$matches))
						if ((int)$matches[1] > $max)
							$max = (int)$matches[1];
				return $max+1;
			}break;
			default:{
				if ($res = parent::__get($nm))
					return $res;
				$this->_load_remote();
				if (isset($this->_remote_data_->$nm))
					return $this->_remote_data_->$nm;
				$cnm = preg_replace('/^Campaign/', '', $nm);
				if ($cnm != $nm){
					$this->_load_campaign();
					if (isset($this->_campaign_data_->$cnm))
						return $this->_campaign_data_->$cnm;
				}
			}break;
		}
		return null;
	}
	
	public function __set($nm,$value){
		switch ($nm){
			case 'PhraseList':{
				if (is_array($value)){
					$this->_phrase_list_ = $value;
					$this->Phrases = json_encode($value);
				}
			}break;
			default:parent::__set($nm,$value);break;
		}
	}
}

/**
 * 
 * @author kras
 * @property IItemPublisher $SitePublisher
 */

class TYandexDirectPublisher extends TCandyModel implements IItemPublisher {
	
/**
 * @var string
 */	
	protected $_conf_default_campaign_ = '';
	
/**
 * @var string
 */	
	protected $_conf_login_ = '';
	
/**
 * @var string
 */	
	protected $_conf_app_id_ = '';
	
/**
 * @var string
 */	
	protected $_conf_app_secret_ = '';

/**
 * @var string
 */	
	protected $_conf_o_auth_token_ = null;
	
/**
 * @var string
 */
	protected $_conf_o_auth_token_expires_ = null;
	
/**
 * @var string
 */	
	protected $_conf_api_url_;
	
/**
 * @var string
 */	
	protected $_conf_geo_targeting_;
	
/**
 * @var bool
 */	
	protected $_bool_auto_broker_ = false;
	
/**
 * @var string
 */	
	protected $_conf_auto_budget_priority_ = "Medium";
	
/**
 * @var string
 */	
	protected $_conf_frontend_url_;
	
/**
 * @var IItemPublisher
 */	
	protected $_ioc_site_publisher_;
	
/**
 * @var IKeyValueRepository
 */	
	protected $_ioc_shop_settings_;
	
	protected function afterConstruct(){
		$this->_conf_classname_ = 'TYandexDirectBanner';
		$this->_array_id_properties_ = array('ProductPage');
	}
	
	public function __set($nm,$value){
		switch ($nm){
			case 'OAuthToken':{
				if (is_null($this->_conf_o_auth_token_) && $this->ShopSettings)
					$this->ShopSettings->SetValue('YandexOAuthToken',$value);
				parent::__set($nm, $value);
			}break;
			case 'OAuthTokenExpires':{
				if (is_null($this->_conf_o_auth_token_expires_) && $this->ShopSettings)
					$this->ShopSettings->SetValue('YandexOAuthTokenExpires',$value);
				parent::__set($nm, $value);
			}break;
			default:parent::__set($nm, $value);break;
		}
	}
	
	public function __get($nm){
		switch ($nm){
			case 'OAuthToken':{
				if (is_null($this->_conf_o_auth_token_) && $this->ShopSettings)
					$this->_conf_o_auth_token_ = $this->ShopSettings->GetValue('YandexOAuthToken');
				if ($this->OAuthTokenExpires){
					$d = new TDate((int)$this->OAuthTokenExpires);
					$cur = new TDate();
					if ($cur->TimeStamp() >= $d->TimeStamp()){
						$this->OAuthToken = false;
						$this->OAuthTokenExpires = null;
					}
				}
				return parent::__get($nm);
			}break;
			case 'OAuthTokenExpires':{
				if (is_null($this->_conf_o_auth_token_expires_) && $this->ShopSettings)
					$this->_conf_o_auth_token_expires_ = $this->ShopSettings->GetValue('YandexOAuthTokenExpires');
				return parent::__get($nm);
			}break;
			default:return parent::__get($nm);break;
		}
	}
	
	public function ObtainToken($code){
		$data = array (
				'grant_type' => 'authorization_code',
				'code' => $code
				);
		
		$data = http_build_query($data);
		
		$context_options = array (
				'http' => array (
						'method' => 'POST',
						'protocol_version' => 1.1,
						//'request_fulluri' => true,
						'header'=> array(
								'Content-type: application/x-www-form-urlencoded',
						//		'Content-Length: '.strlen($data),
								'Authorization: Basic '.base64_encode($this->AppId.':'.$this->AppSecret)
								),
						'content' => $data
				)
		);
		
		$context = stream_context_create($context_options);	
		$result = file_get_contents('https://oauth.yandex.ru/token', false, $context);
		$token = json_decode($result);
		
		if (isset($token->access_token))
			$this->SetToken($token->access_token,isset($token->expires_in)?$token->expires_in:null);
	}
	

	public function SetToken($token,$expires = null){
		if (isset($token))
			$this->OAuthToken = $token;
		if (isset($expires)){
			$t = new TDate();
			$t->Add($expires,TDate::DP_SECOND);
			$this->OAuthTokenExpires = $t->TimeStamp();
		}	
	}
	
	public function NeedsToken(){
		return !$this->OAuthToken;
	}
	
	public function TokenRequestUrl($response_type = 'token',$popup = false){
		return 'https://oauth.yandex.ru/authorize?response_type='.$response_type.'&client_id='.$this->AppId.($popup?'&display=popup':'');
	}

	public function postProcessProperties(){
		parent::postProcessProperties();
		$this->propertyMetas['Article']->ReadOnly = true;
		$this->propertyMetas['Banner']->ReadOnly = true;
		
		try {
			$campaigns = $this->_query_yandex('GetCampaignsList', array());
			if (isset($campaigns->data)){
				$sel = array();
				foreach ($campaigns->data as $c)
					$sel[$c->CampaignID] = $c->Name;
				$this->propertyMetas['Campaign']->SetSelection($sel);
			}
		} catch (Exception $e){
				
		}
	}
	
	public function CheckItemProperty(TNOrmObject $object, TOrmPropertyMeta $property){
		if ($property->Name() == 'Stage'){
			$banner = $this->WrapObject($object);
			$selection = array();
			switch ($banner->Stage){
				case TYandexDirectBanner::ST_NEW:{
					$selection = array(TYandexDirectBanner::ST_NEW => 'New');
					if ($banner->Campaign)
						$selection[TYandexDirectBanner::ST_PUBLISHED] = 'Publish';
				}break;
				case TYandexDirectBanner::ST_PUBLISHED:{
					$selection = array(TYandexDirectBanner::ST_PUBLISHED => 'Publish');
					if ($banner->Banner){
						if ($banner->CampaignStatusShow == 'Yes'){
							if ($banner->StatusBannerModerate == 'Yes')
								$selection[TYandexDirectBanner::ST_RESUMED] = 'Resume';
						} else	
							$selection[TYandexDirectBanner::ST_MODERATE] = 'Moderate';
					}
				}break;
				case TYandexDirectBanner::ST_MODERATE:{
					$selection = array(TYandexDirectBanner::ST_MODERATE => 'Moderate');
					if ($banner->StatusBannerModerate == 'Yes'){
						$selection[TYandexDirectBanner::ST_RESUMED] = 'Resume';
						$selection[TYandexDirectBanner::ST_PUBLISHED] = 'Publish';
					}
				}break;
				case TYandexDirectBanner::ST_RESUMED:{
					$selection = array(TYandexDirectBanner::ST_RESUMED => 'Resumed');
					if ($banner->StatusShow == 'Yes')
						$selection[TYandexDirectBanner::ST_STOPED] = 'Stop';
					$selection[TYandexDirectBanner::ST_PUBLISHED] = 'Republish';
				}break;
				case TYandexDirectBanner::ST_STOPED:{
					$selection = array(TYandexDirectBanner::ST_STOPED => 'Stopped');
					if ($banner->StatusShow == 'No')
						$selection[TYandexDirectBanner::ST_RESUMED] = 'Resume';
					$selection[TYandexDirectBanner::ST_PUBLISHED] = 'Republish';
				}break;
			}
			$property->SetSelection($selection);
		}
		return true;
	}	

/**
 * @param IItem $item
 * @return IItem
 */	
	public function PublishItem(IItem $item,array $data){
		if ($item->Base instanceof TMSProductPage)
			$page = $item;
		else
			$page = $this->SitePublisher->ItemIssue($item);

		if (!$page)
			throw new TYandexDirectException(TYandexDirectException::ERR_NO_SITE_PAGE);
		
		$itemdata = array('ProductPage'=>$page->Id,'Title'=>(string)$item,'Stage'=>TYandexDirectBanner::ST_NEW);
		if ($this->DefaultCampaign)
			$itemdata['Campaign'] = $this->DefaultCampaign;
		$result = $this->CreateItem($itemdata);
		return $result;
	}
/**
 * @param IItem $item
 * @return IItem
 */	
	public function ItemIssue(IItem $item){
		if ($item->Base instanceof TMSProductPage)
			$page = $item;
		else
			$page = $this->SitePublisher->ItemIssue($item);
		
		if ($page){
			$dummy = new TYandexDirectBanner();
			$dummy->ProductPage = $page;
			$dummy = $this->Adapter->Load($dummy);
			if ($dummy instanceof TYandexDirectBanner)
				return $this->WrapObject($dummy);
		}
		return null;	
	}
	
	public function WrapObject($object, array &$caller_stack = array()){
		if ($object instanceof TYandexDirectBanner)
			return new TYandexDirectBannerItem($object, $this);
		return parent::WrapObject($object,$caller_stack);
	}
	
	public function OnItemChange(IItem $item){
		$banner = $this->ItemIssue($item);
		if ($banner){
			$data = array();
			$resumed = false;
			
			if (
					$banner->ProductPage->Base instanceof TMSArticlePage 
					|| 
					$banner->ProductPage->Base instanceof TMSNomenclaturePage
				)
				$resumed = $banner->ProductPage->Product->AllowOrder;
			else 
				$resumed = $banner->ProductPage->IsPublished;
			
			
			if ($banner->Stage == TYandexDirectBanner::ST_RESUMED && !$resumed)
				$data['Stage'] == TYandexDirectBanner::ST_STOPED;
			/*elseif ($banner->Stage == TYandexDirectBanner::ST_STOPED && $resumed)
				$data['Stage'] == TYandexDirectBanner::ST_RESUMED;*/
			
			if (!empty($data))
				$this->EditItem($banner->ItemId, $data);
		}
	}
	
	public function OnItemDelete(IItem $item){
		if ($banner = $this->ItemIssue($item))
			$this->_query_yandex("DeleteBanners", array("CampaignID"=>$banner->Campaign, "BannerIDS" => array($banner->Banner)));
	}
	

	private function utf8($struct){
		foreach ($struct as $key => $value) {
			if (is_array($value)) {
				$struct[$key] = $this->utf8($value);
			}
			elseif (is_string($value)) {
				$struct[$key] = utf8_encode($value);
			}
		}
		return $struct;
	}
		
	private function _query_yandex($method,$params){
		if ($this->OAuthToken){
			$request = array(
				'token'=> $this->OAuthToken,
				'application_id'=> $this->AppId,
				'login'=> $this->Login,
				'method'=> $method,
				'param'=> $this->utf8($params),
				'locale'=> 'ru'
			);
		
			$opts = array('http'=>array(
				'method'=>'POST',
				'header'  => 'Content-type: application/x-www-form-urlencoded',
				'content'=>json_encode($request)
			));
			$res = json_decode(file_get_contents($this->ApiUrl, 0, stream_context_create($opts)));
			if (isset($res->error_code))
				throw new TExposableException($res->error_str.': '.$res->error_detail,$res->error_code);
			return $res;
		}
		return null;
	}
	
	protected function getBannerData($banner){
		$b = $this->_query_yandex("GetBanners", array('BannerIDS' => array($banner)));
		if (isset($b->data) && is_array($b->data) && !empty($b->data))
			return (object)$b->data[0];
		return (object)array();
	}
	
	public function GetBannerInfo(TYandexDirectBanner $banner){
		if ($banner->Banner)
			return $this->getBannerData($banner->Banner);
		return (object)array();
	}
	
	public function GetCampaignInfo($campaign){
		if ($campaign){
			$res = $this->_query_yandex("GetCampaignParams", array('CampaignID' => $campaign));
			if (isset($res->data))
				return $res->data;
		}
		return (object)array();
	}
	
	protected function preEditItem($id,array $data, $origin){		
		if (isset($data['Stage'])){
			$old = $this->GetItem($id);
			if ($old->Stage != $data['Stage']){
				switch ($data['Stage']){
					case TYandexDirectBanner::ST_PUBLISHED:{												
						$opts = array('http'=>array('method'=>"GET"));
												
						$href = json_decode(file_get_contents($this->FrontendUrl.'?service=json&method=ItemUrl&class=TMSProductPage&id='.$old->ProductPage->ItemId().'&slug='.$old->ProductPage->Slug, 0, stream_context_create($opts)));
						if (!$href)
							throw new TYandexDirectException(TYandexDirectException::ERR_NO_PAGE_URL);				

						$phrases = array();
						$list = $data['PhraseList']?$data['PhraseList']:$old->PhraseList;
						foreach ($list as $phrase_id=>$phrase){
							if (preg_match('/^'.$id.'_\d+/', $phrase_id))
								$phrase_id = 0;
							$phrases[] = array(
            								'PhraseID' => $phrase_id,
            								'Phrase' => $phrase,
            								'IsRubric' => 'No',
            								'AutoBroker' => $this->AutoBroker?'Yes':'No',
            								'AutoBudgetPriority' => $this->AutoBudgetPriority
         							);
						}
						
						$res = $this->_query_yandex("CreateOrUpdateBanners", array(
   							array(
      							'BannerID' => $old->Banner?$old->Banner:0,
      							'CampaignID' => $data['Campaign']?$data['Campaign']:$old->Campaign,
      							'Title' => $data['Title']?$data['Title']:$old->Title,
      							'Text' => $data['Text']?$data['Text']:$old->Text,
      							'Href' => $href,
      							'Geo' => $this->GeoTargeting,
      							'Phrases' => $phrases,
      							'MinusKeywords' => TConvertions::ConvertToArray($data['MinusKeywords']?$data['MinusKeywords']:$old->MinusKeywords)
   							)
						));
						
						if (isset($res->data) && !empty($res->data)){
							$data['Banner'] = $res->data[0];
							if ($banner = $this->getBannerData($res->data[0])){
								$data['PhraseList'] = array();
								foreach ($banner->Phrases as $phrase)
									$data['PhraseList'][$phrase->PhraseID] = $phrase->Phrase;
								if ($banner->StatusBannerModerate == 'Yes' && $banner->StatusShow == 'Yes')
									$data['Stage'] = TYandexDirectBanner::ST_RESUMED;
							} else throw new TYandexDirectException(TYandexDirectException::ERR_NO_BANNER);
						}
					}break;
					case TYandexDirectBanner::ST_MODERATE:{
						$res = $this->_query_yandex("ModerateBanners", array('CampaignID' => $old->Campaign,'BannerIDS' => array($old->Banner)));
					}break;
					case TYandexDirectBanner::ST_RESUMED:{
						$res = $this->_query_yandex("ResumeBanners", array('CampaignID' => $old->Campaign,'BannerIDS' => array($old->Banner)));
					}break;
					case TYandexDirectBanner::ST_STOPED:{
						$res = $this->_query_yandex("StopBanners", array('CampaignID' => $old->Campaign,'BannerIDS' => array($old->Banner)));
					}break;
				}
			}
		}
		
		if (isset($data['PhraseList'])){
			$data['Phrases'] = json_encode($data['PhraseList']);
			unset($data['PhraseList']);
		}
		
		return $data;
	}
}